package com.lx.util;

import com.lx.annotation.Note;
import com.lx.util.LX.RequestMethod;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublisher;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpRequest.Builder;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

public class LXRequest {
    private Builder builder = HttpRequest.newBuilder().timeout(Duration.ofSeconds(30));

    public LXRequest(String url){
        builder.uri(URI.create(url)).headers("Content-Type", "application/json");
    }

    public LXRequest header(String name, String value) {
        builder.header(name,value);
        return this;
    }

    public LXRequest headers(String... params) {
        builder.headers(params);
        return this;
    }

    public LXRequest headers(Map<String,String> headers) {
        if (LX.isNotEmpty(headers)){
            headers.forEach((name,value)->{
                builder.header(name,value);
            });
        }
        return this;
    }

    public LXRequest timeout(Duration duration) {
        builder.timeout(duration);
        return this;
    }

    public LXRequest body(Object object) {
        builder.POST(BodyPublishers.ofString(LX.toJSONString(object)));
        return this;
    }
    public LXRequest body(String body) {
        builder.POST(BodyPublishers.ofString(body));
        return this;
    }

    public LXRequest body(BodyPublisher bodyPublisher) {
        builder.POST(bodyPublisher);
        return this;
    }

    public LXRequest put(BodyPublisher bodyPublisher){
        builder.PUT(bodyPublisher);
        return this;
    }

    @Note("form表单")
    public LXRequest formdata(Map<String, String> params) {
        return formdata(params,"","&",false,false);
    }

    @Note("form表单")
    public LXRequest formdata(Map<String, String> params,String exclude, String connStr, boolean urlEncode, boolean skipEmpty) {
        String form = LX.createLinkString(params,exclude,connStr,urlEncode,skipEmpty);
        builder.POST(BodyPublishers.ofString(form)).headers("Content-Type", "application/x-www-form-urlencoded");
        return this;
    }

    @Note("form表单")
    public LXRequest formdata(String form) {
        builder.POST(BodyPublishers.ofString(form)).headers("Content-Type", "application/x-www-form-urlencoded");
        return this;
    }

    public LXRequest delete() {
        return this;
    }

    public LXRequest file(File file) throws Exception {
        file(null,file.getName(),new FileInputStream(file),null);
        return this;
    }
    public LXRequest file(InputStream inputStream) throws Exception {
        file(null,null,inputStream,null);
        return this;
    }
    public LXRequest file(byte[] bytes) throws Exception {
        file(null,null,new ByteArrayInputStream(bytes),null);
        return this;
    }
    private LXRequest file(String name, String fileName, InputStream fileStream, Map<String,String> formDatas) throws Exception {
        uploadFile(LX.ifEmpty(name,"file"),LX.ifEmpty(fileName,""),fileStream,formDatas);
        return this;
    }


    @Note("调用接口返回字符串")
    public String send(){
        return send(BodyHandlers.ofString()).body();
    }
    @Note("调用接口")
    private <T> HttpResponse<T> send(HttpResponse.BodyHandler<T> responseBodyHandler){
        try {
            return HttpClient.newHttpClient().send(builder.build(), responseBodyHandler);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Note("调用接口返回字符串")
    public CompletableFuture<HttpResponse<String>> sendAsync(){
        return sendAsync(BodyHandlers.ofString());
    }
    @Note("调用接口")
    private <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpResponse.BodyHandler<T> responseBodyHandler){
        try {
            return HttpClient.newHttpClient().sendAsync(builder.build(), responseBodyHandler);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static final String LINE_SEPARATOR = "\r\n";
    private void uploadFile(String name, String fileName, InputStream fileStream, Map<String,String> formDatas) throws Exception {
        String boundary = "===" + System.currentTimeMillis() + "===";
        builder.headers("Content-Type", "multipart/form-data; boundary=" + boundary);
        try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream, "UTF-8"), true);){
            if (LX.isNotEmpty(formDatas)){
                for (Map.Entry<String,String> e : formDatas.entrySet()){
                    writer.append("--").append(boundary).append(LINE_SEPARATOR);
                    writer.append("Content-Disposition: form-data; name=\"").append(e.getKey()).append("\"").append(LINE_SEPARATOR);
                    writer.append(LINE_SEPARATOR).append(e.getValue()).append(LINE_SEPARATOR).flush();
                }
            }
            // 文件部分
            writer.append("--").append(boundary).append(LINE_SEPARATOR);
            writer.append("Content-Disposition: form-data; name=\"").append(name).append("\"; filename=\"").append(fileName).append("\"").append(LINE_SEPARATOR);
            writer.append("Content-Type: ").append("application/octet-stream").append(LINE_SEPARATOR);
            writer.append(LINE_SEPARATOR).flush();
            // 写入文件内容
            LX.inputStreamToOutputStream(fileStream, outputStream);
            writer.append(LINE_SEPARATOR).flush();
            // 结束分隔符
            writer.append("--").append(boundary).append("--").append(LINE_SEPARATOR).flush();
            BodyPublisher bodyPublisher = BodyPublishers.ofByteArray(outputStream.toByteArray());
            builder.POST(bodyPublisher);
        }
    }

    static {
        try {
            //https 默认信任证书
            trustAllHttpsCertificates();
            System.setProperty("jdk.tls.maxCertificateChainLength", "100"); // 解决证书过长
            System.setProperty("jdk.tls.maxHandshakeMessageSize", "65536"); // 解决证书过长
            HttpsURLConnection.setDefaultHostnameVerifier((urlHostName, session) -> true);
        } catch (Exception e) {
        }
    }

    private static void trustAllHttpsCertificates() throws NoSuchAlgorithmException, KeyManagementException {
        TrustManager[] trustAllCerts = new TrustManager[1];
        trustAllCerts[0] = new TrustAllManager();
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }

    private static class TrustAllManager implements X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public void checkServerTrusted(X509Certificate[] certs, String authType) { }
        public void checkClientTrusted(X509Certificate[] certs, String authType) { }
    }
}
